package per.cy.common.util;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

/**
 * @author cy
 * date 2024/10/16
 */
public class SM2LocalUtil {

    private static final Log log = LogFactory.get();
    private static final Base64.Encoder BASE64_ENCODER = Base64.getEncoder();
    private static final Base64.Decoder BASE64_DECODER = Base64.getDecoder();

    private SM2LocalUtil() {
    }

    static {
        // 在应用启动时加载 BouncyCastleProvider
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 创建非对称加密的公钥和私钥文件
     *
     * @param publicKeyPath  公钥文件的保存路径
     * @param privateKeyPath 私钥文件的保存路径
     * @param charset        指定的字符集，用于保存密钥文件
     */
    public static void createKey(String publicKeyPath, String privateKeyPath, String charset) {
        // 添加BouncyCastle安全提供者，以支持更多的加密算法
//        Security.addProvider(new BouncyCastleProvider());

        try {
            // 初始化密钥对生成器，使用椭圆曲线算法EC
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("EC", "BC");

            // 初始化密钥对生成器的密钥长度
            keyPairGenerator.initialize(256);

            // 生成密钥对
            KeyPair keyPair = keyPairGenerator.genKeyPair();

            // 获取并编码公钥和私钥，以便于文件存储
            byte[] publicKey = keyPair.getPublic().getEncoded();
            byte[] privateKey = keyPair.getPrivate().getEncoded();

            // 使用Base64编码，并以指定字符集保存公钥和私钥到文件
            FileUtils.writeFileContent(publicKeyPath, BASE64_ENCODER.encodeToString(publicKey), charset);
            FileUtils.writeFileContent(privateKeyPath, BASE64_ENCODER.encodeToString(privateKey), charset);

        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            // 处理算法不可用异常
            log.error("Error creating key pair: {}", e.getMessage(), e);
            throw new RuntimeException("Error creating key pair", e);
        }
    }


    /**
     * 根据公钥路径和字符集获取公钥对象
     *
     * @param publicKeyPath 公钥文件的路径
     * @param charset       字符集名称，用于读取公钥文件
     * @return 返回公钥对象
     * <p>
     */
    public static PublicKey getPublicKey(String publicKeyPath, String charset) {
        try {
            // 读取公钥文件内容
            String publicKeyContent = FileUtils.getFileContent(publicKeyPath, charset);
            // 将Base64编码的公钥内容解码为字节数组
            byte[] decodePublicKey = BASE64_DECODER.decode(publicKeyContent);
            // 使用解码后的公钥字节数组创建X509EncodedKeySpec对象
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodePublicKey);
            // 通过KeyFactory生成公钥对象
            KeyFactory keyFactory = KeyFactory.getInstance("EC", "BC");
            return keyFactory.generatePublic(keySpec);
        } catch (NoSuchAlgorithmException e) {
            // 记录并抛出NoSuchAlgorithmException异常
            log.error("公钥文件的 NoSuchAlgorithmException: {}, 字符集: {}", publicKeyPath, charset, e);
            throw new RuntimeException("公钥文件的 NoSuchAlgorithmException: " + publicKeyPath, e);
        } catch (NoSuchProviderException e) {
            // 记录并抛出NoSuchProviderException异常
            log.error("公钥文件的 NoSuchProviderException: {}, 字符集: {}", publicKeyPath, charset, e);
            throw new RuntimeException("公钥文件的 NoSuchProviderException: " + publicKeyPath, e);
        } catch (InvalidKeySpecException e) {
            // 记录并抛出InvalidKeySpecException异常
            log.error("公钥文件的 InvalidKeySpecException: {}, 字符集: {}", publicKeyPath, charset, e);
            throw new RuntimeException("公钥文件的 InvalidKeySpecException: " + publicKeyPath, e);
        }
    }


    /**
     * 根据私钥文件路径和字符集获取PrivateKey对象
     *
     * @param privateKeyPath 私钥文件的路径
     * @param charset        指定的字符集，用于读取私钥文件内容
     * @return 返回生成的PrivateKey对象
     */
    public static PrivateKey getPrivateKey(String privateKeyPath, String charset) {
        try {
            // 读取私钥文件内容
            String privateKeyContent = FileUtils.getFileContent(privateKeyPath, charset);
            // 将Base64编码的私钥内容解码为字节数组
            byte[] decodePrivateKey = BASE64_DECODER.decode(privateKeyContent);
            // 使用解码后的私钥字节数组创建PKCS8EncodedKeySpec对象
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodePrivateKey);
            return KeyFactory.getInstance("EC", "BC").generatePrivate(keySpec);
        } catch (NoSuchAlgorithmException | NoSuchProviderException e) {
            log.error("不支持的算法或提供者: {}", e.getMessage(), e);
            throw new RuntimeException("不支持的算法或提供者: ", e);
        } catch (InvalidKeySpecException e) {
            log.error("无效的密钥规范: {}", e.getMessage(), e);
            throw new RuntimeException("无效的密钥规范: ", e);
        }
    }


    /**
     * 使用SM2公钥进行加密
     *
     * @param publicKey 公钥
     * @param data      待加密的数据
     * @return 加密后的数据
     */
    public static byte[] encrypt(PublicKey publicKey, byte[] data) {
        try {
            // 创建SM2加密实例
            Cipher cipher = Cipher.getInstance("SM2", "BC");
            // 初始化加密模式
            cipher.init(Cipher.ENCRYPT_MODE, publicKey);
            // 执行加密并返回结果
            return cipher.doFinal(data);
        } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
            log.error("初始化加密实例失败: " + e.getMessage(), e);
            throw new RuntimeException("初始化加密实例失败: " + e.getMessage());
        } catch (InvalidKeyException e) {
            log.error("无效的公钥: " + e.getMessage(), e);
            throw new RuntimeException("无效的公钥: " + e.getMessage());
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            log.error("加密过程中发生错误: " + e.getMessage(), e);
            throw new RuntimeException("加密过程中发生错误: " + e.getMessage());
        }
    }


    /**
     * 使用SM2算法和私钥对加密数据进行解密
     *
     * @param privateKey 用于解密的私钥
     * @param encryptedData 已加密的数据
     * @return 解密后的数据
     */
    public static byte[] decrypt(PrivateKey privateKey, byte[] encryptedData) {
        try {
            // 创建SM2解密实例
            Cipher cipher = Cipher.getInstance("SM2", "BC");
            // 初始化解密模式
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            // 执行解密并返回结果
            return cipher.doFinal(encryptedData);
        } catch (NoSuchAlgorithmException | NoSuchProviderException | NoSuchPaddingException e) {
            log.error("初始化解密实例失败: " + e.getMessage(), e);
            throw new RuntimeException("初始化解密实例失败: " + e.getMessage());
        } catch (InvalidKeyException e) {
            log.error("无效的私钥: " + e.getMessage(), e);
            throw new RuntimeException("无效的私钥: " + e.getMessage());
        } catch (IllegalBlockSizeException | BadPaddingException e) {
            log.error("解密过程中发生错误: " + e.getMessage(), e);
            throw new RuntimeException("解密过程中发生错误: " + e.getMessage());
        }
    }



}
